package tommysmapeditor.editors;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;

import tommysmapeditor.editors.bo.FrameDescription;
import tommysmapeditor.editors.bo.HitBoxDescription;
import tommysmapeditor.editors.bo.HitPointDescription;
import tommysmapeditor.editors.bo.HitBoxDescription.HitBoxType;
import tommysmapeditor.editors.helpers.CreateResized;
import tommysmapeditor.editors.helpers.SwtImageHelper;
import tommysmapeditor.editors.widget.AnimFrameCanvas;
import tommysmapeditor.helper.FileUtils;

public class AnimationAndHitboxEditor extends EditorPart {

	public static String ID = "tommysmapeditor.editors.AnimationAndHitboxEditor";
	
	//c++ resource management :p
	List<Image> toDispose = new ArrayList<Image>();

	
	//the real business data we edit is here
	List<AnimFrameCanvas> frames= new ArrayList<AnimFrameCanvas>();;
	
	// this is wher we add canvas
//	private Composite absoluteBase;
	
	private Composite _framePane;
	
	public int zoomFactor = 1;

	
	//default value : 16 blank pixels outside to put hitboxes slightly out
	public int frameMargin =16;
	
	private Text txtNewHitbox;

	private Combo currentHitboxCombo;

	private Button addFrameBtn;
	
	
	public AnimationAndHitboxEditor() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void doSave(IProgressMonitor monitor) {
		// TODO Auto-generated method stub

	}

	@Override
	public void doSaveAs() {
		// TODO Auto-generated method stub

	}

	@Override
	public void init(IEditorSite site, IEditorInput input)
			throws PartInitException {
		setSite(site);
		setInput(input);
	}

	@Override
	public boolean isDirty() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isSaveAsAllowed() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void createPartControl(Composite parent) {
		
//		//container to be able to work in absolute
//		final  ScrolledComposite scrolled = new ScrolledComposite(parent,SWT.V_SCROLL | SWT.H_SCROLL);
//		absoluteBase = new Composite(scrolled, SWT.NONE);
//		scrolled.setContent(absoluteBase);	

		parent.setLayout(new FormLayout());
		
		
		createContainerForFrames(parent);
		
		
//		_parent = parent;
		
		//button bar
		final  Composite scrolledButtons = new Composite(parent,SWT.BORDER);
//		scrolledButtons.setBounds(700, 0, 300, 600);
		
		FormData fd_sb = new FormData();
		fd_sb.right= new FormAttachment(100,0);
		fd_sb.left= new FormAttachment(70,0);
		fd_sb.top = new FormAttachment(0,0);
		fd_sb.bottom = new FormAttachment(100,0);
		scrolledButtons.setLayoutData(fd_sb);
		
		
		scrolledButtons.setLayout(new FormLayout());
		
		// the button bar is split in 2 composites
		
		//the upper buttons take 50 percent DOESN't WORK
//		Composite upperButtons = new Composite(scrolledButtons,SWT.BORDER);
//		
//		FormData fd_upBut = new FormData();
//		fd_upBut.top= new FormAttachment(0,10);
//		fd_upBut.left= new FormAttachment(0,10);
//		fd_upBut.right= new FormAttachment(100,-10);
//		fd_upBut.bottom= new FormAttachment(30,0);
//		
//		upperButtons.setLayoutData(fd_upBut);
//		
//		upperButtons.setLayout(new FormLayout());
		
		createAddFrameBtn(
//				upperButtons
				scrolledButtons
				
				);

		Label lblZoom = new Label(scrolledButtons,SWT.NONE);
		lblZoom.setText("zoom:");
		
		FormData fd_lblz = new FormData();
		fd_lblz.top = new FormAttachment(addFrameBtn,10);
		fd_lblz.left = new FormAttachment(0,0);
		lblZoom.setLayoutData(fd_lblz);
		
		
		final Combo zoomCombo = new Combo(scrolledButtons,SWT.READ_ONLY);
		zoomCombo.setItems(new String[]{"1","2","3"});
		zoomCombo.setText("1");
		zoomCombo.addSelectionListener(new SelectionListener() {
			
			public void widgetSelected(SelectionEvent e) {
				int selected = Integer.valueOf(zoomCombo.getText());
				System.out.println("zoom selected: "+selected);
				
				System.out.println("previously selected : " + zoomFactor);
				int prev = zoomFactor;
				zoomFactor = selected;
				if( prev!=selected){
					reinitAnimFrameCanvas();
				}
				
			}
			
			public void widgetDefaultSelected(SelectionEvent e) {
				// TODO Auto-generated method stub
				
			}
		});		

		FormData fd_zcmb = new FormData();
		fd_zcmb.top= new FormAttachment(addFrameBtn,10);
		fd_zcmb.left= new FormAttachment(lblZoom,10);

		zoomCombo.setLayoutData(fd_zcmb);
		
		
		Button newContactHitBoxBtn = new Button(scrolledButtons,SWT.NONE);
		newContactHitBoxBtn.setText("new CONTACT hitbox for frame");
		newContactHitBoxBtn.addSelectionListener(new SelectionListener(){
			public void widgetSelected(SelectionEvent e) {
				System.out.println("txt content: "+txtNewHitbox.getText());
				if(txtNewHitbox.getText().trim().length()!=0){
					System.out.println("adding new hit box to framedesc");
					getEditedCanvas()._current.addHitbox(HitBoxType.CONTACT,txtNewHitbox.getText());
				
				fillHBComboFromData();
				}else{
					System.out.println("no name to create");
				}
				
			}
			public void widgetDefaultSelected(SelectionEvent e) {
				
			}
		});

		FormData fd_nchbb = new FormData();
		fd_nchbb.top= new FormAttachment(lblZoom,10);
		fd_nchbb.left= new FormAttachment(0,10);
		
		newContactHitBoxBtn.setLayoutData(fd_nchbb);
		
		
		txtNewHitbox = new Text(scrolledButtons,SWT.BORDER);

		FormData fd_tnhb = new FormData();
		fd_tnhb.top= new FormAttachment(lblZoom,10);
		fd_tnhb.left= new FormAttachment(newContactHitBoxBtn,10);

		txtNewHitbox.setLayoutData(fd_tnhb);
		
		Label lblCurrentHitbox = new Label(scrolledButtons,SWT.NONE  );
		lblCurrentHitbox.setText("current hitbox:");

		FormData fd_lchb = new FormData();
		fd_lchb.top= new FormAttachment(newContactHitBoxBtn,40);
		fd_lchb.left= new FormAttachment(0,10);

		lblCurrentHitbox.setLayoutData(fd_lchb);
		
		
		currentHitboxCombo = new Combo(scrolledButtons,SWT.READ_ONLY);
		currentHitboxCombo.addSelectionListener(new SelectionAdapter(){
			public void widgetSelected(SelectionEvent e) {
				String selection = currentHitboxCombo.getText();
				System.out.println("selection "+selection);
				getEditedCanvas().selectCurrentlyEdited( selection );
			}
		});

		FormData fd_chbcmb = new FormData();
		fd_chbcmb.top= new FormAttachment(newContactHitBoxBtn,40);
		fd_chbcmb.left= new FormAttachment(lblCurrentHitbox,10);

		currentHitboxCombo.setLayoutData(fd_chbcmb);
		
		
		
		Button deleteHitBox = new Button(scrolledButtons,SWT.NONE);
		deleteHitBox.setText("delete");
		deleteHitBox.addSelectionListener(new SelectionAdapter(){
			public void widgetSelected(SelectionEvent e) {
				String selected = currentHitboxCombo.getText();
				System.out.println("trying to delete hitbox of current frame:"+selected);
				
				for(HitBoxDescription hbd: getEditedCanvas()._current.hitBoxes){
					if(selected.equals(hbd.name)){
						getEditedCanvas()._current.hitBoxes.remove(hbd);
						getEditedCanvas().currentHitbox=null;
						System.out.println("found and deleted");
						getEditedCanvas().redraw();
						
						//emptying currenthitbox combo
						//FIXME most probably duplicated
						String[] comboStrings = new String[getEditedCanvas()._current.hitBoxes.size()];
						for(int i=0; i< getEditedCanvas()._current.hitBoxes.size();i++){
							comboStrings[i]=getEditedCanvas()._current.hitBoxes.get(i).name;
							
						}
						currentHitboxCombo.setItems(comboStrings);
						
						break;
					}
				}
				
			}
		});

		FormData fd_delBtn = new FormData();
		fd_delBtn.top= new FormAttachment(lblCurrentHitbox,10);
		fd_delBtn.left= new FormAttachment(0,10);

		deleteHitBox.setLayoutData(fd_delBtn);
		

		Button saveAnim = new Button(scrolledButtons,SWT.NONE);
		saveAnim.setText("save");
		saveAnim.addSelectionListener(new SelectionAdapter(){
			public void widgetSelected(SelectionEvent e) {
				saveEditorContent();
			}
		});

		FormData fd_saveAnimBut = new FormData();
		fd_saveAnimBut.top= new FormAttachment(deleteHitBox,40);
		fd_saveAnimBut.left= new FormAttachment(0,10);

		saveAnim.setLayoutData(fd_saveAnimBut);
		
		
		Button loadAnim = new Button(scrolledButtons,SWT.NONE);
		loadAnim.setText("load");
		loadAnim.addSelectionListener(new SelectionAdapter(){
			public void widgetSelected(SelectionEvent e) {
				loadEditorContent();
			}
		});

		FormData fd_loadAnimBtn = new FormData();
		fd_loadAnimBtn.top= new FormAttachment(saveAnim,10);
		fd_loadAnimBtn.left= new FormAttachment(0,10);
		
		loadAnim.setLayoutData(fd_loadAnimBtn);
		
		Button exportInvertedXFrames = new Button(scrolledButtons,SWT.NONE);
		exportInvertedXFrames.setText("exportInvertedXFrames NOT FUCKING WORKING");
		
		FormData fd_eixfBtn = new FormData();
		fd_eixfBtn.top= new FormAttachment(loadAnim,10);
		fd_eixfBtn.left= new FormAttachment(0,10);
		
		exportInvertedXFrames.setLayoutData(fd_eixfBtn);
		exportInvertedXFrames.addSelectionListener(
				new SelectionAdapter() {
					public void widgetSelected(SelectionEvent e) {
						exportXinvertedFramesInDir();
					}
				}
				);
	}

	
	/**
	 * to batch export inverted pics
	 */
	protected void exportXinvertedFramesInDir() {
		DirectoryDialog fd = new DirectoryDialog(Display.getCurrent().getActiveShell());
		fd.setText("select export frames dir ");
		String path = fd.open();
		
		
		int i=0;
		for( AnimFrameCanvas afc: frames ){
			ImageData toSave = SwtImageHelper.reverseX(afc._current.base.getImageData());
			
			ImageLoader loader = new ImageLoader();
			loader.data = new ImageData[] {toSave};
			loader.save(path+"/"+"frame"+i+".bmp", SWT.IMAGE_BMP);
			i++;
		}
	}

	private void createContainerForFrames(Composite parent) {
		//we put the anim frames in a scrolled composite that takes up rest of the space
		_framePane = new ScrolledComposite(parent,SWT.BORDER);
		
		FormData fd_fp = new FormData();
		fd_fp.left= new FormAttachment(0,10);
		fd_fp.top= new FormAttachment(0,10);
		fd_fp.bottom= new FormAttachment(100,-10);
		fd_fp.right= new FormAttachment(70,-10);
		_framePane.setLayoutData(fd_fp);
	}

	/**
	 * loads saved anim/hb editor project
	 */
	protected void loadEditorContent() {
		DirectoryDialog fd = new DirectoryDialog(Display.getCurrent().getActiveShell());
		
		String directoryPath =fd.open();
		
		System.out.println(directoryPath);
		File temptativeDir=new File(directoryPath);
		if(temptativeDir.exists()){

			System.out.println("importing from path:"+directoryPath);
			importAnim(directoryPath);
		}
	}


	/**
	 * read definitions text file and fills editor accordingly
	 * @param directoryPath
	 */
	private void importAnim(String directoryPath) {
		try {
			FileReader fr = new FileReader(directoryPath+"/"+"animhbdef");
			BufferedReader br = new BufferedReader(fr);
			String readFrameNumber =br.readLine();
			int frameNum = Integer.parseInt(readFrameNumber);
			for(int i=0;i<frameNum;i++){
				String fileName =br.readLine();
				System.out.println(fileName);
				String timeCode=br.readLine();
				System.out.println(timeCode);
				String numHitBox = br.readLine();
				System.out.println(numHitBox);
				
				
				FrameDescription fdesc = buildFrameDescFromImageFilePath(directoryPath+"/"+fileName);

				fdesc.timeCode= Long.parseLong(timeCode);
				
				int nbHb= Integer.parseInt(numHitBox);
				
				for(int j= 0;j<nbHb;j++){
					String hitBoxType = br.readLine();
					System.out.println(hitBoxType);
					String hitBoxName = br.readLine();
					System.out.println(hitBoxName);
					String hbx = br.readLine();
					String hby = br.readLine();
					String hbw = br.readLine();
					String hbh = br.readLine();
					
					HitBoxDescription hb = new HitBoxDescription();
					hb.type=HitBoxType.valueOf(hitBoxType);
					hb.name=hitBoxName;
					hb.coordinates =new Rectangle(
							Integer.parseInt(hbx), 
							Integer.parseInt(hby), 
							Integer.parseInt(hbw), 
							Integer.parseInt(hbh)
							);
					
					fdesc.hitBoxes.add(hb);
				}

				String numHitPoints = br.readLine();
				System.out.println(numHitPoints);

				int nbHp= Integer.parseInt(numHitPoints);
				
				for(int j= 0;j<nbHp;j++){
					String hitPointType = br.readLine();
					System.out.println(hitPointType);
					String hitPointName = br.readLine();
					System.out.println(hitPointName);
					String hpx = br.readLine();
					String hpy = br.readLine();
					
					HitPointDescription hp = new HitPointDescription();
					hp.type=HitPointDescription.HitPointType.valueOf(hitPointType);
					hp.name=hitPointName;
					hp.coordinates =new Point(
							Integer.parseInt(hpx), 
							Integer.parseInt(hpy) 
							);
					
					fdesc.hitPoints.add(hp);
				}
				
				
				
				addCanvasForFdesc(fdesc);
				
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		
	}

	private void createAddFrameBtn(Composite upperButtons) {
		addFrameBtn = new Button(
//				scrolledButtons
				upperButtons
				,SWT.NONE);
		addFrameBtn.setText("add frame");
//		addFrameBtn.setBounds(10, 70, 100, 20);
		addFrameBtn.addSelectionListener(new SelectionListener(){
			public void widgetSelected(SelectionEvent e) {
				System.out.println("opening file dialog to add frame");
				FileDialog fd = new FileDialog(Display.getCurrent().getActiveShell());
				String filePath =fd.open();
				
				FrameDescription fdesc = buildFrameDescFromImageFilePath(filePath);

				addCanvasForFdesc(fdesc);
				
				
				
				
			}
			public void widgetDefaultSelected(SelectionEvent e) {
				
			}
		});
		
		//first button up of composite
		FormData fd = new FormData();
		fd.top= new FormAttachment(0,10);
		fd.left= new FormAttachment(0,10);
//		fd.right= new FormAttachment(100,-10);

		addFrameBtn.setLayoutData(fd);
		
	}
	private FrameDescription buildFrameDescFromImageFilePath(
			String filePath) {
		Image newFrame = new Image(Display.getCurrent(),filePath);
	
//		System.out.println(CreateResized.resize(filePath, 8, 8, 2));
		
		toDispose.add( newFrame);
		
		FrameDescription fdesc = new FrameDescription();
		fdesc.base = newFrame;
		
		double dblzoom = 2; 

		final int dblwidth = fdesc.base.getBounds().width; 
		final int dblheight = fdesc.base.getBounds().height; 

		fdesc.baseX2 = new Image(Display.getDefault(), fdesc.base.getImageData().scaledTo((int)(dblwidth * dblzoom),(int)(dblheight * dblzoom))); 
		
		toDispose.add(fdesc.baseX2);
		
		double trizoom = 3; 

		fdesc.baseX3 = new Image(Display.getDefault(), fdesc.base.getImageData().scaledTo((int)(dblwidth * trizoom),(int)(dblheight * trizoom))); 
		
		toDispose.add(fdesc.baseX3);

		
		
		fdesc.sourceFilePath=filePath;
		return fdesc;
	}

	protected void saveEditorContent() {
		DirectoryDialog fd = new DirectoryDialog(Display.getCurrent().getActiveShell());
		
		String directoryPath =fd.open();
		
		System.out.println(directoryPath);
		File temptativeDir=new File(directoryPath);
		if(temptativeDir.exists()){

			System.out.println("exporting to path:"+directoryPath);
			exportAnim(directoryPath);
		}
		
	}

	
	/**w
	 * writes the anim / hitbox definitions file 
	 * in the directory provided and copies all pics
	 * @param directoryPath
	 */
	private void exportAnim(String directoryPath) {
		try {
			FileWriter saveFile = null;
				saveFile = new FileWriter(directoryPath+"/"+"animhbdef");
			
			BufferedWriter bw = new BufferedWriter(saveFile);
			
			//writing frames number
			bw.write(String.valueOf(frames.size()) );
			bw.newLine();
			
			
			for( int i=0;i<frames.size(); i++ ){
			//copying first file
				String destFrameName = "frame"+i+".bmp";
				File in = new File ( frames.get(i)._current.sourceFilePath );
				File out = new File( directoryPath+"/"+ destFrameName );
				FileUtils.copyFile(in, out);
				
				//writing name of file
				bw.write(destFrameName);
				bw.newLine();
				
				//writing time code 
				//TODO hardcoded for now
				bw.write(String.valueOf(20));
				bw.newLine();
				
				
				//writing number of hitboxes def
				bw.write( String.valueOf(frames.get(i)._current.hitBoxes.size()) );
				bw.newLine();
				
				//writing hitbox defs
				for( int j=0;j<frames.get(i)._current.hitBoxes.size();j++){
					HitBoxDescription hbd = frames.get(i)._current.hitBoxes.get(j);
					//hit box type
					bw.write( hbd.type.toString() );
					bw.newLine();
					//hit box name
					bw.write( hbd.name.toString() );
					bw.newLine();

					//hitbox coordinates
					bw.write( String.valueOf(hbd.coordinates.x) );
					bw.newLine();
					bw.write( String.valueOf(hbd.coordinates.y) );
					bw.newLine();
					//hitbox size
					bw.write( String.valueOf(hbd.coordinates.width) );
					bw.newLine();
					bw.write( String.valueOf(hbd.coordinates.height) );
					bw.newLine();
					
				}
				//writing number of hitpoints def
				bw.write( String.valueOf(frames.get(i)._current.hitPoints.size()) );
				bw.newLine();
				
				//writing hitpoints defs
				for( int j=0;j<frames.get(i)._current.hitPoints.size();j++){
					HitPointDescription hpd = frames.get(i)._current.hitPoints.get(j);
					//hit box type
					bw.write( hpd.type.toString() );
					bw.newLine();
					//hit box name
					bw.write( hpd.name.toString() );
					bw.newLine();

					//hitbox coordinates
					bw.write( String.valueOf(hpd.coordinates.x) );
					bw.newLine();
					bw.write( String.valueOf(hpd.coordinates.y) );
					bw.newLine();
					
				}
			}
			
			bw.flush();
			bw.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

	/**
	 * copies info from current edited anim frame canvas 
	 * to select box
	 * 
	 * 
	 */
	protected void fillHBComboFromData() {
		
		String[] hbs = new String[getEditedCanvas()._current.hitBoxes.size()]; 
		for(int i= 0;i< getEditedCanvas()._current.hitBoxes.size();i++){
			hbs[i]=getEditedCanvas()._current.hitBoxes.get(i).name;
			System.out.println(hbs[i]);
		}
		
		currentHitboxCombo.setItems(hbs);
	}

	/**
	 * on zoom factor change we need to modify canvas props
	 */
	protected void reinitAnimFrameCanvas() {
		
		int currentY = 10;
		
		for(AnimFrameCanvas afc : frames){
			afc.resizeCanvas(currentY,zoomFactor,frameMargin);
			
			currentY= afc.getLowerY();
		}
		
		
	}

	protected void addCanvasForFdesc(final FrameDescription fdesc) {
		int initY=10;
		if(frames.size()>0){
			initY=frames.get(frames.size()-1).getLowerY()+10;
		}
		
		
		AnimFrameCanvas cvs = new AnimFrameCanvas(
				_framePane, 
				SWT.NONE,
				fdesc,
				initY,
				this,
				zoomFactor,
				frameMargin);
		
		frames.add(cvs);
		
//		absoluteBase.setSize(absoluteBase.computeSize(SWT.DEFAULT, SWT.DEFAULT));
		
		
	}

	@Override
	public void setFocus() {
		// TODO Auto-generated method stub

	}

	
@Override
public void dispose() {
	super.dispose();
	
	System.out.println("disposing frames");
	
	for(Image img  : toDispose){
		img.dispose();
	}
}


/**
 * called when one sub canvas is clicked, 
 * we needd to flag the new edited and reset others 
 * @param animFrameCanvas
 */
public void newCanvasEdit(AnimFrameCanvas animFrameCanvas) {

	for(AnimFrameCanvas afc:frames){
		if(afc== animFrameCanvas){
			System.out.println("new edited:"+afc);
			afc.currentEdited=true;
		}else{
			System.out.println("not edited:"+afc);
			afc.currentEdited=false;
		}
		afc.redraw();
	}
	
	//we also fill the checkboxes combo with approriate hitbox list
	fillHBComboFromData();
}
	
/**
 * to get current clicked,
 * to edit metadata
 * @return
 */
public AnimFrameCanvas getEditedCanvas(){
	for(AnimFrameCanvas afc : frames){
		if(afc.currentEdited)return afc;
	}
	
	//if no edited
	return null;
}

}
